#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <vector>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <dirent.h>
#include "ins_nnie_interface.h"
#include "Tensor.h"
#include "util.h"
#include <opencv2/opencv.hpp>
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/core.hpp"
#include "opencv2/dnn.hpp"
#define _BSD_SOURCE
#include "sample_nnie_api.h"
#include <iostream>

#define debugflag 0
#define LimitTime 40*60*3
extern "C"
{
//int fb_write(unsigned char *pShowScreen);
#include "libavcodec/avcodec.h"
#include "libavdevice/avdevice.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"
#include "libavutil/avutil.h"
#include "libavutil/time.h"
#include "libswscale/swscale.h"
#include "libavutil/pixdesc.h"
};

#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avdevice.lib")
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avfilter.lib")
#pragma comment(lib, "swscale.lib")
#pragma comment(lib, "swresample.lib")
#pragma comment(lib, "postproc.lib")

using namespace cv;
using namespace std;

void* ppHandle; 
float ConfidenceThreshold=0.5;
/*
char *cls_names[] = {"background", "person", "bicycle", "car", "motorbike", "aeroplane", "bus", "train", "truck", "boat",
                         "traffic light", "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep",
                         "cow", "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase",
                         "frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket",
                         "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "donut", "apple", "sandwich",
                         "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "sofa", "pottedplant",
                         "bed", "diningtable", "toilet", "vmonitor", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave",
						 "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush"};
*/
char *cls_names[] = {"background","jing", "jin", "jiHebei", "jinShanxi", "meng", "liao", "jiLin", "hei", "hu","su", "zhe", "wan", "min", "gan", "lu", "yu", "e", "xiang","yue", "gui", "qiong", "yu", "chuan", "gui", "yun", "zang", "shan","ganSu", "qing", "ning","xinJiang","gang","ao","shi","linShi","xue" , "jincha","A","B","C","D","E" , "F" , "G" ,"H" , "I" , "J" , "K" , "L" ,"M", "N","O","P" ,"Q" , "R" , "S" , "T" ,"U" , "V" , "W" , "X" , "Y" , "Z", "1", "2", "3","4", "5", "6", "7", "8", "9","0"};
int yolo_init(const char *yolo_model_path)
{
    if (access(yolo_model_path,F_OK)==-1)
    {
        printf("yolov3_model is not exists!\n");
        return -1;
    }
    ppHandle = new NNIE();
    NNIE* yolov3_mnas=(NNIE *)ppHandle;
    yolov3_mnas->init(yolo_model_path);
    return 0;
}



int yolov3_inference(void* pHandle, Mat& imgRGB , string& forStore,int* StoreID,int &countID)
{
    NNIE* yolov3_mnas=(NNIE *)pHandle;
    int img_width = imgRGB.cols;
    int img_height = imgRGB.rows;
    int step = imgRGB.step;
    int c = imgRGB.channels();
    int num_classes = 73;
    int kBoxPerCell = 3;
//	cout<< step << "  "<<c<<endl;

    unsigned char *data = (unsigned char *)malloc(sizeof(unsigned char) * img_width * img_height * c);
    unsigned char *data1 = (unsigned char *)imgRGB.data;
    int count = 0;

    for (int k = 0; k < c; k++)
    {
        for (int i = 0; i < img_height; i++)
        {
            for (int j = 0; j < img_width; j++)
            {
                data[count++] = data1[i * step + j * c + k];
            }
        }
    }

    yolov3_mnas->run(data);
    Tensor output0 = yolov3_mnas->getOutputTensor(0);
    Tensor output1 = yolov3_mnas->getOutputTensor(1);
    Tensor output2 = yolov3_mnas->getOutputTensor(2);

    int feature_index0 = 0;
    int feature_index1 = 1;
    int feature_index2 = 2;

    //float conf_threshold = 0.1;
	float conf_threshold = 0.01;
    //float nms_threshold = 0.4;
	float nms_threshold = 0.6;
    int is_nms = 1;

    std::vector<int> ids;
    std::vector<cv::Rect> boxes;
    std::vector<float> confidences;

    const std::vector<std::vector<cv::Size2f>> anchors = {
        {{116, 90}, {156, 198}, {373, 326}},
        {{30, 61}, {62, 45}, {59, 119}},
        {{10, 13}, {16, 30}, {33, 23}}};

    parseYolov3Feature(img_width,
                       img_height,
                       num_classes,
                       kBoxPerCell,
                       feature_index0,
                       conf_threshold,
                       anchors[0],
                       output0,
                       ids,
                       boxes,
                       confidences);

    parseYolov3Feature(img_width,
                       img_height,
                       num_classes,
                       kBoxPerCell,
                       feature_index1,
                       conf_threshold,
                       anchors[1],
                       output1,
                       ids,
                       boxes,
                       confidences);

    parseYolov3Feature(img_width,
                       img_height,
                       num_classes,
                       kBoxPerCell,
                       feature_index2,
                       conf_threshold,
                       anchors[2],
                       output2,
                       ids,
                       boxes,
                       confidences);

    std::vector<int> indices;

    std::vector<ObjectDetection> detection_results;
cout<< "boxes.size() = "<<boxes.size()<<endl;

    if (is_nms)
    {
        cv::dnn::NMSBoxes(boxes, confidences, conf_threshold, nms_threshold, indices);
    }
    else
    {
        for (int i = 0; i < boxes.size(); ++i)
        {
            indices.push_back(i);
        }
    }

    //{"data":["class":"dog","score":,0,4,"x":1,"y":2,"w":1,"h":2],[]}
	char temp[4096]={0};
	std::string r=temp;
	cout<< "indices.size() = "<<indices.size()<<endl;

	struct detect_table{
		float xmin;
		int cls_id;
		string cls_name;
		detect_table *next;
	};
	int countTable = 0;
	detect_table* resultTable = new detect_table;
	detect_table* tmp = resultTable;
	for (size_t i = 0; i < indices.size(); ++i)
    {
        int idx = indices[i];
        cv::Rect box = boxes[idx];

        // remap box in src input size.
        auto remap_box = RemapBoxOnSrc(cv::Rect2d(box), img_width, img_height);
        ObjectDetection object_detection;
        object_detection.box = remap_box;
        object_detection.cls_id = ids[idx] + 1;  //
        object_detection.confidence = confidences[idx];
        detection_results.push_back(std::move(object_detection));

        float xmin = object_detection.box.x;
        float ymin = object_detection.box.y;
        float xmax = object_detection.box.x + object_detection.box.width;
        float ymax = object_detection.box.y + object_detection.box.height;
        float confidence = object_detection.confidence;
        int cls_id = object_detection.cls_id;
        
		char *cls_name = cls_names[cls_id];

		if(confidence>ConfidenceThreshold)
		{
			printf("%d %s %.3f %.3f %.3f %.3f %.3f\n", cls_id, cls_name, confidence, xmin, ymin, xmax, ymax);
			tmp -> xmin = xmin;
			tmp -> cls_id = cls_id;
			tmp -> cls_name = cls_name;
			tmp -> next = new detect_table;
			tmp = tmp->next;
			countTable++;
			//forStore += cls_name;
		}
		memset(temp,0,sizeof(temp));
		r+=temp;
    	if (i!= indices.size()-1)
    	{
    		r+=",";
    	}
		cv::rectangle(imgRGB, cv::Point(xmin,ymin),cv::Point(xmax,ymax),cv::Scalar(255,0,0), 3);		
    }
   // cout << "tmp->cls_name\t" << tmp->cls_name << endl;
	cout << "countTable = " << countTable << endl;
	countID = countTable;
    delete tmp;
    tmp = resultTable;
    int count1 = countTable;
   /* while(count1--)
    {
    	//cout << "count1=" << count1 << endl;
    	cout<< "node\t " << tmp->cls_name << "\t" << tmp->xmin << "\t" << tmp->next <<endl;
    	tmp = tmp->next;
    }*/

 //   cout << "out of the table\n";

    //set the next part of the last block NULL for bubble sort
    count1 = countTable;
    tmp = resultTable;
    while(count1--)
    {
    	if(count1 == 0) {tmp->next = NULL;}//cout << "next of the last block set to NULL" << endl;}
    	tmp = tmp->next;

    }

    count1 = countTable;
        tmp = resultTable;
        while(count1--)
    	{
            	//cout << "count1=" << count1 << endl;
    		cout<< "node\t " << tmp->cls_id << tmp->cls_name << "\t" << tmp->xmin << "\t" << tmp->next <<endl;
    		tmp = tmp->next;
    	}

	detect_table *current = resultTable ;
	detect_table *next_current = NULL;
	float tmpFloat;
	string tmpStr;
	int tmpInt;

	//count1 = countTable - 1;
    //cout << "count1 = " << count1 << endl;
    for(int i = 0; i < countTable -1; i++)
    {
    	//cout << "i =" << i << endl;
    	current = resultTable;
    	next_current = current ->next;
    	//cout << "next current \t" << next_current << endl;
    	for(int j=0; j< countTable-i-1  ;j++ )
    	{
    		//cout << "assert I am in the inner loop" << endl;
    		 if(current->xmin > next_current->xmin)
    		 {
    			 tmpFloat = current->xmin;
    			 current->xmin = next_current->xmin;
    			 next_current->xmin = tmpFloat;

    			 tmpStr = current->cls_name;
    			 current->cls_name = next_current->cls_name;
    			 next_current->cls_name = tmpStr;

    			 tmpInt = current-> cls_id;
    			 current->cls_id = next_current->cls_id;
    			 next_current->cls_id = tmpInt;
    			// cout << "change happened!\n" ;

    			 //cout<< "node\t " << current->cls_name << "\t" << current->xmin << "\t" << current->next <<endl;
    		 }
    		current = current->next;
    		next_current = next_current->next;
    	 }
    }

    count1 = countTable;
    tmp = resultTable;
   // cout << "resultTable = " << resultTable << endl;

    //StoreID = new int[countTable];
    //while(count1--)
	for(int i = 0; i<countTable; i++)
    {
        	//cout << "count1=" << count1 << endl;
		//cout<< "node\t " << tmp->cls_name << "\t" << tmp->xmin << "\t" << tmp->next <<endl;
		forStore += tmp->cls_name;
		StoreID[i] = tmp->cls_id;
		cout << "cls_id \t" << StoreID[i] << endl;
		tmp = tmp->next;
	}

	for(int i = 0; i<countTable; i++)
	{
		cout << "StoreID \t" << StoreID[i] << endl;
	}

  /*  detect_table *head = new detect_table;
    head -> next = resultTable;

    for(int i = 0 ; i< countTable-1 ; i++)
    {
    	int num = countTable - i - 1;
    	detect_table *current = head -> next ;
    	detect_table *next_current = current -> next;
    	detect_table *tail = head ;
    	while(num--)
    	{
    		cout << "current xmin: " << current->xmin <<'\t'
    				<< "next current xmin: " << next_current ->xmin << endl;
    		if(current -> xmin > next_current -> xmin ){
    			current -> next = next_current -> next ;
    			next_current -> next = current;
    			tail -> next = next_current;

    		//cout << "bubble:\t" << current->cls_name <<endl;

    			next_current = tmp;
    			next_current = current;
    			current = tmp;
    			}

			tail = tail->next;
			current = current->next;
			next_current = next_current -> next;

    	}
    }*/

    //resultTable = head -> next;
    //delete head;
/*
    cout << "assert i am out of bubble" <<endl;
    count1 = countTable - 1;
    tmp = resultTable;
    detect_table *tmp1 = tmp->next;
    forStore = tmp -> cls_name;
    while(count1--){
    	forStore += tmp1->cls_name;
    	tmp1 = tmp1 -> next;
    }
*/

    tmp = resultTable;
    detect_table *tmp1 = tmp->next;
    cout << "before delete the table\ncountTable=\t" << countTable << endl;
    while(countTable--)
    {
    	delete tmp;
    	//tmp = tmp1;
    	//tmp1 = tmp1->next;
    	tmp = tmp->next;
    }

	r+="]}";
//20220106 modified: free mmz
    //yolov3_mnas->finish();
    imwrite("./result/precision.jpg",imgRGB);
    cout << "assert I am in yolorun\n" <<endl;
    resize(imgRGB,imgRGB,Size(800,480) );
    //fb_write((unsigned char*) imgRGB.ptr(0) );
    free(data),data=NULL;
    return 0;
}

//int yolo_run(unsigned char *input_yuv420_data, int input_w,int input_h)
//int yolo_run(char* filename, int input_w,int input_h)
int yolo_run(cv::Mat img, int input_w,int input_h, string& forStore,int* StoreID,\
		int &countID)
{
/*
    Mat imgRGB;
    Mat imgRect;
    Mat imgYUV(input_h + input_h/2, input_w, CV_8UC1, input_yuv420_data);
    cvtColor(imgYUV,imgRGB,COLOR_YUV2BGR_I420);
*/
	

    resize(img, img, cv::Size(416, 416));
    std::vector<Rect> obj_post;
    //yolov3_inference(ppHandle, imgRGB);
    yolov3_inference(ppHandle, img, forStore,StoreID,countID);
    //img.release();
    //video.release();

    return 0;
}


int yolo_run_yuv(unsigned char *input_yuv420_data, int input_w,int input_h, string& forStore, int *StoreID,\
		int &countID)
{

    Mat imgRGB;
    Mat imgYUV(input_h + input_h/2, input_w, CV_8UC1, input_yuv420_data);
    cvtColor(imgYUV,imgRGB,COLOR_YUV2BGR_I420);
    resize(imgRGB, imgRGB, cv::Size(416, 416));
    imwrite("sample_vio_picture.jpg",imgRGB);
    yolov3_inference(ppHandle, imgRGB,forStore,StoreID,countID);
    imgRGB.release();
    imgYUV.release();

    return 0;
}


int yolo_unit()
{
    if(ppHandle)
    {
         delete((NNIE*)ppHandle); ppHandle = NULL;
    }
    return 0; 
}
